#pragma once
#include "lib/table.h"
#include "lib/cstring.h"
#include "lib/conutils.h"

#define STUDENTS_BIN "students.bin"
#define MAX_CHARS_LEN  15

typedef struct {
	char name[MAX_CHARS_LEN+1];    //姓名
	char id[MAX_CHARS_LEN+1];      //学号
	unsigned age;   //年龄
	unsigned javaOOP;   //javaOOP
	unsigned advancedMath;  //高等数学
	unsigned dataStructure;     //数据结构
	unsigned sum;       //总和
	float average;      //平均
} Student; //学生信息结构

#define Student_new() (Student*)malloc(sizeof(Student))

#define Student_free(stup) do { \
	free(stup); \
	stup = NULL; \
} while(0);

#define Student_freeall(stus) do { \
	for (kvp* in_pairs(stus, p)) { \
		free(p->val); \
		kvp_unset(p); \
	} \
} while(0);

#define Student_print(stu) printf("\n║姓名: %s\t年龄: %u\n║学号: %s\n║\n║javaOOP: %u\n║高等数学: %u\n║数据结构:%u\n║合计: %u\n║平均: %g\n\n", stu->name, stu->age, stu->id, stu->javaOOP, stu->advancedMath, stu->dataStructure, stu->sum, stu->average)


// 添加学生
void st_add(table st) {
	string name = String.input("输入学生姓名：");
	while (name->length > MAX_CHARS_LEN) {
		printf("*姓名不能超过 %d 个字符！(当前 %lu)", MAX_CHARS_LEN, name->length);
		ifcontinue();
		name = String.input("输入学生姓名：");
	}
	
	string id = String.input("输入学生学号：");
	while (id->length > MAX_CHARS_LEN) {
		printf("*学号不能超过 %d 个字符！(当前 %lu)", MAX_CHARS_LEN, id->length);
		ifcontinue();
		id = String.input("输入学生学号：");
	}
	
	if (tb_find(st, id->cstr) != -1) {
		printf("学号:%s 已存在\n", id->cstr);
		return;
	}
	
	Student *stu = Student_new();
	strcpy(stu->name, name->cstr);
	strcpy(stu->id, id->cstr);
	
	stu->age = String.toInt(String.input("输入年龄："));
	stu->javaOOP = String.toInt(String.input("输入javaOOP成绩："));
	stu->advancedMath = String.toInt(String.input("输入高等数学成绩："));
	stu->dataStructure = String.toInt(String.input("输入数据结构成绩："));
	
	stu->sum = stu->javaOOP + stu->advancedMath + stu->dataStructure;
	stu->average = stu->sum/3;
	
	puts("学生信息如下：");
	Student_print(stu);
	printf("确定要录入吗？\nY) 是\t*) 否\n请选择操作: ");
	if (!agree()) {
		free(stu);
		puts("操作已取消");
		return;
	}
	
	tb_set(st, stu->id, stu);
	puts("学生已录入");
}

// 查找一个学生
Student *st_find(table st) {
	string key = String.input("输入姓名或学号：");
	
	Student *stu = tb_get(st, key->cstr); // 先按id找
	if (!stu) 
		for (kvp* in_pairs(st, p)) // 逐个比对姓名
			if (String.new(((Student*)p->val)->name) == key)
				stu = p->val;
	
	if (!stu) {
		puts("没有这个学生");
		return NULL;
	}
	
	puts("找到学生信息如下：");
	Student_print(stu);
	return stu;
}

// 删除一个学生
void st_del(table st) {
	Student *stu = st_find(st);
	if (!stu) return;
	
	printf("确定要删除吗？\nY) 是\t*) 否\n请选择操作: ");
	if (!agree()) {
		puts("操作已取消");
		return;
	}
	
	tb_del(st, stu->name);
	free(stu);
}

// 展示所有学生
void st_show(table st) {
	int count = tb_len(st);

	if (count == 0) {
		puts("没有学生信息");
		return;
	}

	puts("所有学生信息如下：");
	for (kvp* in_pairs(st, p)) {
		Student_print(((Student*)p->val));
	}
	printf("共 %d 位学生\n", count);
}

// 修改一个学生的信息
void st_mod(table st) {
	Student *stu = st_find(st);
	if (!stu) return;
	
	while (1) {
		puts("====选择要修改的项目====");
		puts("1) 姓名     4) javaOOP");
		puts("2) 年龄     5) 高等数学");
		puts("3) 学号     6) 数据结构");
		puts("━━━━━━━━━━━━━━━━━━━━━━");
		puts("*) 保存并退出");

		printf("选择(1-%d), 或其他字符退出: ", 6);
		int op = getchar() - '0';
		clearstdin();
		
		switch (op) {
			case 1: {
				string name = String.input("输入学生姓名：");
				while (name->length > MAX_CHARS_LEN) {
					printf("*姓名不能超过 %d 个字符！(当前 %lu)", MAX_CHARS_LEN, name->length);
					ifcontinue();
					name = String.input("输入学生姓名：");
				}
				strcpy(stu->name, name->cstr);
			} break;
			case 2:
				stu->age = String.toInt(String.input("输入年龄："));
				break;
			case 3: {
				string id = String.input("输入学生学号：");
				while (id->length > MAX_CHARS_LEN) {
					printf("*学号不能超过 %d 个字符！(当前 %lu)", MAX_CHARS_LEN, id->length);
					ifcontinue();
					id = String.input("输入学生学号：");
				}
	
				if (tb_find(st, id->cstr) != -1) {
					printf("学号:%s 已存在\n", id->cstr);
					break;
				}
				strcpy(stu->id, id->cstr);
			} break;
			case 4:
				stu->javaOOP = String.toInt(String.input("输入javaOOP成绩："));
				stu->sum = stu->javaOOP + stu->advancedMath + stu->dataStructure;
				stu->average = stu->sum/3;
				break;
			case 5:
				stu->advancedMath = String.toInt(String.input("输入高等数学成绩："));
				stu->sum = stu->javaOOP + stu->advancedMath + stu->dataStructure;
				stu->average = stu->sum/3;
				break;
			case 6:
				stu->dataStructure = String.toInt(String.input("输入数据结构成绩："));
				stu->sum = stu->javaOOP + stu->advancedMath + stu->dataStructure;
				stu->average = stu->sum/3;
				break;
			default:
				puts("修改后的信息如下：");
				Student_print(stu);
				return;
		}
	}
}

// 存入序列化数据到二进制文件
void st_save(table st) {
	FILE*f = fopen(STUDENTS_BIN, "wb");
	for (kvp* in_pairs(st, p)) {
		fwrite(p->val, sizeof(Student), 1, f); // 遍历写入
	}
	fclose(f);
	
	puts("保存完成");
}

// 从二进制文件拉取数据
void st_read(table st) {
	touch(STUDENTS_BIN); // 确保文件存在
	
	FILE*f = fopen(STUDENTS_BIN, "rb");
	fseek(f, 0, SEEK_END);
	long size = ftell(f);

	// 如果文件大小小于结构体大小，视为读取失败
	if (size < sizeof(Student)) {
		puts("读取失败");
		fclose(f);
		return;
	}
	
	int stu_count = size/sizeof(Student);
	rewind(f);
	
	Student *data = (Student*)malloc(size);
	fread(data, sizeof(Student), stu_count, f);
	fclose(f);
	
	for (int i=0; i<stu_count; i++) {
		tb_set(st, data[i].id, data+i);
	}
	puts("读取完成");
}